﻿using System;
using System.Data;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security.Cryptography;
using OccupierArchiver.Properties;

namespace OccupierArchiver.DataRepository
{
    internal static class DataTables
    {
        private static RijndaelManaged _encryptor;
        private static DataSet _dbStandByYou;
        private static string _database;
        private static bool _initialized;

        static DataTables()
        {
            TblMessage = null;
        }

        internal static DataTable TblMessage { get; private set; }

        internal static void InitializeTables()
        {
            _dbStandByYou = new StandByYou();
            _database = Settings.Default.Database;
            InitializeEncryptor();
            _initialized = true;

            if (!File.Exists(_database))
            {
                ConstructTables();
                SerializeTables();
            }
            else
            {
                // ConstructTables();
                try
                {
                    DeserializeTables();
                }
                catch (Exception)
                {
                    RenewDatabase();
                    InitializeTables();
                }
            }
        }

        private static void InitializeEncryptor()
        {
            var key = Settings.Default.EncryptorKey;
            var iv = Settings.Default.EncryptorIV;

            if (key != null && key.GetType() == typeof(byte[])
                && iv != null && iv.GetType() == typeof(byte[]))
            {
                _encryptor = new RijndaelManaged
                {
                    Key = (byte[])key,
                    IV = (byte[])iv
                };
            }
            else
            {
                RenewDatabase();
                InitializeTables();
            }
        }

        private static void RenewDatabase()
        {
            Settings.Default.Database = Guid.NewGuid().ToString();

            using (var rm = new RijndaelManaged())
            {
                Settings.Default.EncryptorKey = rm.Key;
                Settings.Default.EncryptorIV = rm.IV;
            }

            Settings.Default.Save();
        }

        private static void DeserializeTables()
        {
            if (!_initialized) throw new InvalidOperationException();
            _dbStandByYou.RemotingFormat = SerializationFormat.Binary;
            var fs = new FileStream(_database, FileMode.Open);
            var cs = new CryptoStream(fs, _encryptor.CreateDecryptor(_encryptor.Key, _encryptor.IV),
                CryptoStreamMode.Read);
            var fmt = new BinaryFormatter();
            _dbStandByYou = (DataSet)fmt.Deserialize(cs);
            LinkTables();
            cs.Close();
            fs.Close();
            cs.Dispose();
            fs.Dispose();
        }

        private static void SerializeTables()
        {
            if (!_initialized) throw new InvalidOperationException();
            _dbStandByYou.RemotingFormat = SerializationFormat.Binary;
            var fs = new FileStream(_database, FileMode.Create);
            var cs = new CryptoStream(fs, _encryptor.CreateEncryptor(_encryptor.Key, _encryptor.IV),
                CryptoStreamMode.Write);
            var fmt = new BinaryFormatter();
            fmt.Serialize(cs, _dbStandByYou);
            cs.FlushFinalBlock();
            cs.Close();
            fs.Close();
            cs.Dispose();
            fs.Dispose();
        }

        private static void ConstructTables()
        {
            _dbStandByYou = new StandByYou();
            LinkTables();
        }

        private static void LinkTables()
        {
            TblMessage = _dbStandByYou.Tables["Message"];
        }

        private static void CommitTables()
        {
            if (!_initialized) throw new InvalidOperationException();
            _dbStandByYou.AcceptChanges();
        }

        private static void HoldTables()
        {
            if (!_initialized) throw new InvalidOperationException();
            _dbStandByYou.RejectChanges();
        }

        private static void ResetTables()
        {
            if (!_initialized) throw new InvalidOperationException();
            _dbStandByYou.Reset();
        }

        private static void CleanupTables()
        {
            if (!_initialized) throw new InvalidOperationException();
            _dbStandByYou.Dispose();
        }

        internal static void AcceptChanges()
        {
            CommitTables();
            SerializeTables();
        }

        internal static void UninitializeTables()
        {
            CommitTables();
            SerializeTables();
            CleanupTables();
            _initialized = false;
        }
    }
}
